<template>
  <div
    class="paginated-virtual-tree"
    v-loading="loading"
    element-loading-background="transparent"
  >
    <virtual-tree
      ref="virtualTree"
      v-bind="$attrs"
      v-on="$listeners"
      :node-key="nodeKey"
      :data="treeData"
      :option="curProps"
      :lazy="!!load"
      :load="lazyLoad"
      :page-size="originPageSize"
      :always-top="false"
      @load-more="handlePageChange"
    />
  </div>
</template>
<script>
import virtualTree from './tree';

export default {
  name: 'PaginatedVirtualTree',

  components: {
    virtualTree,
  },

  props: {
    data: {
      type: Array,
      default: () => ([]),
    },
    nodeKey: {
      type: String,
    },
    parentKey: {
      type: String,
      default: 'parent',
    },
    option: Object,
    totalCount: {
      type: Number,
      default: 0,
    },
    pageSize: {
      type: Number,
      default: 10,
    },
    getRootTree: {
      type: Function,
      default: () => {},
      required: true,
    },
    load: {
      type: Function,
      default: null,
    },
    autoLoad: {
      type: Boolean,
      default: true,
    },
  },

  data () {
    return {
      treeData: [],
      tempUpdateData: null,
      getPromise: null,
      updatePromise: null,
      loading: false,
      pageIndex: 0,
      total: this.totalCount || 0,
      originPageSize: Math.max(this.pageSize, 10),
      currentNodeKey: null,
    };
  },

  computed: {
    curProps () {
      return Object.assign({
        key: 'id',
        label: 'label',
        children: 'children',
        disabled: 'disabled',
        className: 'className',
        expandDisabled: 'expandDisabled',
      }, this.option || {});
    },
    tree () {
      if (!this.$refs.virtualTree) return;
      return this.$refs.virtualTree;
    },
  },

  watch: {
    data () {
      if (!this.autoLoad) this.initData();
    },
  },

  mounted () {
    this.bindMethods();
    this.initData();
  },

  methods: {
    bindMethods () {
      const methods = [
        'scrollTo',
        'setCheckedNodes',
        'setCurrentNode',
        'clearCurrentNode',
        'filter',
        'setCheckedAll',
      ];
      methods.forEach(method => {
        this[method] = this.$refs.virtualTree?.[method];
      });
    },

    render () {
      if (!this.$refs.virtualTree) return;
      this.$refs.virtualTree.initNodes(this.treeData);
      this.$refs.virtualTree.initHeight();
    },

    redraw (data) {
      this.treeData = [].concat(data);
    },

    doAllExpand () {
      (this.tree?.nodes || []).forEach(node => {
        if (!node.isLeaf && !node.expanded) {
          node.toggleExpand();
        }
      });
    },

    initData () {
      if (!this.data?.length && this.autoLoad) {
        this.getTreeList(true);
      } else {
        this.treeData = [].concat(this.data);
        this.pageIndex = Math.floor(this.treeData.length / this.originPageSize);
        this.total = this.totalCount;
        if (this.pageIndex > 0 && (this.treeData.length % this.originPageSize === 0 || !this.total)) this.pageIndex--;
        if (this.total && this.treeData?.[this.treeData?.length - 1]?.isLoadMore) {
          this.treeData.splice(this.treeData.length - 1, 1);
        }
        if (this.total > (this.pageIndex + 1) * this.originPageSize && this.originPageSize !== -1) {
          const loadMoreBtn = this.createLoadMoreNode();
          this.treeData.splice(this.treeData.length, -1, loadMoreBtn);
        }
      }
    },

    getNode (key) {
      return this.tree?._find(key)?.node;
    },

    isAllLoaded (data) {
      if (!data) {
        return (this.pageIndex + 1) * this.originPageSize >= this.total;
      } else {
        const node = this.tree.nodeMap[data[this.curProps.key]];
        if (node.data.leaf) return true;
        const { pageIndex, pageSize } = node.$node;
        const { total } = node.data;
        return (pageIndex + 1) * pageSize >= total;
      }
    },

    // 强制刷新根数据(data为空)/叶子节点(data)的父节点数据/refreshCurrent为true直接刷新data指向节点
    refresh (data, refreshCurrent, force = false) {
      if (!data) {
        return this.getTreeList(true, force ? null : (this.pageIndex + 1) * this.originPageSize).then(res => {
          return res;
        });
      } else {
        const node = this.tree.nodeMap[data[this.curProps.key]];
        const parent = refreshCurrent ? node : node?.$node.parent;
        if (parent?.data?.leaf && !refreshCurrent) {
          return Promise.resolve();
        }
        if (!parent) {
          return this.getTreeList(true, force ? null : (this.pageIndex + 1) * this.originPageSize);
        } else {
          parent.pageIndex = 0;
          parent.loaded = false;
          parent.loading = true;
          parent.data.pageIndex = 0;
          parent.data.loaded = false;
          parent.data.loading = true;
          parent.data.children = [];
          return this.tree.lazyLoad(parent);
        }
      }
    },

    // 获取叶子节点
    async lazyLoad (node) {
      this.currentNodeKey = null;
      const { pageIndex, pageSize } = node;

      let loadMoreBtn;
      let loadMoreNode;
      let tempData = (node?.children || []).map(o => o.data);
      const tempLastIndex = tempData?.length - 1;
      if (tempData?.[tempLastIndex]?.isLoadMore) {
        loadMoreBtn = tempData[tempLastIndex];
      }

      if (loadMoreBtn) {
        loadMoreNode = this.tree.nodeMap.get(loadMoreBtn[this.curProps.key]);
        loadMoreNode.loading = true;
      }

      const data = await this.load(node, pageIndex, pageSize);
      node.data.total = data.total;
      node.data.pageIndex = pageIndex;

      if (loadMoreBtn) {
        loadMoreNode.loading = false;
      }

      tempData = tempData.concat(data.data);

      // loadMoreBtn = this.createLoadMoreNode();
      if (data.total > (pageIndex + 1) * pageSize && pageSize !== -1) {
        if (!loadMoreBtn) loadMoreBtn = this.createLoadMoreNode();
        tempData.splice(tempData.length, -1, loadMoreBtn);
      }
      if (tempLastIndex > 0) {
        tempData.splice(tempLastIndex, 1);
      }
      return tempData;
    },

    // 获取根节点下的第一层数据
    getTreeList (refresh = false, tempPageSize) {
      if (!this.getRootTree) return Promise.resolve();
      if (this.loading) return this.getPromise || Promise.resolve();
      if (refresh) {
        this.pageIndex = 0;
        this.total = 0;
        this.treeData = [];
      }
      let scrollNode = null;
      this.loading = true;
      this.getPromise = this.getRootTree(
        this.pageIndex,
        tempPageSize || this.originPageSize,
      ).then(({ data, total }) => {
        if (tempPageSize) {
          this.pageIndex = Math.floor(tempPageSize / this.originPageSize) - 1;
        }

        this.total = total;

        if (this.treeData?.[this.treeData?.length - 1]?.isLoadMore) {
          this.treeData.splice(this.treeData.length - 1, 1);
        }

        this.treeData = this.treeData.concat(data);

        if (this.pageIndex) {
          scrollNode = this.tree?.nodes[this.tree.nodes.length - 2];
        }

        if (this.total > (this.pageIndex + 1) * this.originPageSize && this.pageSize !== -1) {
          const loadMoreBtn = this.createLoadMoreNode();
          this.treeData.splice(this.treeData.length, -1, loadMoreBtn);
        }

        if (scrollNode && this.tree) {
          setTimeout(() => {
            this.tree.scrollTo(scrollNode.key || scrollNode.data[this.nodeKey || 'key']);
            // this.tree.scrollTo(scrollNode.key || scrollNode.data[this.nodeKey || 'key'] || scrollNode.data?.id);
          }, this.$attrs.currentNodeKey || this.$attrs?.['current-node-key'] ? 150 : 0);
        }
        return { data: [].concat(this.treeData), total };
      }).catch((err) => {
        if (this.pageIndex > 0 && !refresh) {
          this.pageIndex--;
        }
        console.error('err', err);
      }).finally(() => {
        this.loading = false;
      });

      return this.getPromise;
    },

    createLoadMoreNode () {
      return {
        [this.nodeKey ?? 'id']: new Date().getTime(),
        [this.curProps.label]: '加载更多',
        [this.curProps.className]: 'is-load-more',
        isLoadMore: true,
        leaf: true,
        loaded: true,
      };
    },

    // 分页操作处理
    handlePageChange (node) {
      if (node && node.parent && node.level > 0) {
        const { parent } = node;
        parent.pageIndex++;
        parent.loaded = false;
        parent.data.loaded = false;
        this.tree.lazyLoad(parent);
      } else {
        // 最外层
        this.pageIndex++;
        this.getTreeList();
      }
    },

    // 保留已加载的节点外，拼接新加入的节点/子节点数据
    updateTreeData (data) {
      if (this.loading) {
        /* this.tempUpdateData = data;
        return new Promise((resolve) => {
          this.updatePromise = resolve;
        }); */
        return new Promise((resolve) => {
          this.getPromise.then(() => {
            this.updateTreeData(data);
            resolve(this.treeData);
          });
        });
      }
      this.treeData = this.updateData(this.treeData, data);
      // this.treeData = this.updateData(this.treeData, data);
      this.pageIndex = Math.floor(this.treeData.length / this.originPageSize);
      // this.tempUpdateData = null;

      /* if (this.updatePromise) {
        this.updatePromise();
        this.updatePromise = null;
      } */
      return Promise.resolve(this.treeData);
    },

    // 递归拼接工具方法
    updateData (originData, data) {
      if (originData?.[originData?.length - 1]?.isLoadMore) {
        originData.splice(originData.length - 1, 1);
      }
      const tempData = [];
      (data || []).forEach((item, index) => {
        if (originData?.[index]?.[this.nodeKey || 'id'] === item[this.nodeKey || 'id'] && originData?.[index]?.loaded && !originData[index].leaf) {
          // DFS递归
          if (item.children?.length) this.updateData(originData[index].children, item.children);
          if (originData[index].children?.length > item.children?.length) {
            tempData.push(originData[index]);
          } else {
            item.loaded = true;
            item.pageIndex = Math.floor(item.children?.length / this.originPageSize);
            if (item.children?.length % this.originPageSize === 0) item.pageIndex--;
            if (!item.children) {
              item.leaf = true;
            }
            tempData.push(item);
          }
        } else {
          if (!item.children) {
            item.leaf = true;
          } else if (item.children?.length) {
            this.updateData([], item.children);
          }
          tempData.push(item);
        }
      });
      originData = tempData;
      return originData;
    },
  },
};
</script>
<style lang="scss" scoped>
.paginated-virtual-tree {
  /deep/.virtual-tree-node__more {
    .virtual-tree-node__wrapper {
      justify-content: center;
    }

    .virtual-tree-node__expand-icon {
      display: none;
    }

    .virtual-tree-node__content {
      flex: inherit;
      width: auto !important;
    }
  }

  /deep/.icon-spin + .virtual-tree-node__content {
    margin-left: 4px;
  }
}
</style>
